package yun.web.springmvc.ext;

import cn.hutool.core.bean.BeanDesc;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.BeansException;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.DispatcherServlet;
import yun.web.enjoy.kit.Kv;
import yun.web.springmvc.ReqAtta;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import static org.springframework.web.servlet.HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE;
import static org.springframework.web.servlet.HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE;

public class MvcHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {

    Log log = LogFactory.get("MvcHandlerMethodArgumentResolver");

    /*------------------------
          author: 张天笑
          date: 2019/3/3
          time: 22:23
       开发者不要作死往参数上放奇怪的类型
    -------------------------*/
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return true;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

        HttpServletRequest req = (HttpServletRequest) webRequest.getNativeRequest();
        HttpServletResponse resp = (HttpServletResponse) webRequest.getNativeResponse();
        Object value = null;
        String parameterName = parameter.getParameterName();
        Class<?> parameterType = parameter.getParameterType();
        if (parameterType == HttpServletRequest.class) {
            return req;
        }
        if (parameterType == HttpServletResponse.class) {
            return resp;
        }

        // Kv会注入所有来自请求的字符参数
        if (parameterType == Kv.class) {
            Kv kv = new Kv();
            Enumeration<String> parameterNames = req.getParameterNames();
            while (parameterNames.hasMoreElements()) {
                String k = parameterNames.nextElement();
                kv.put(k, req.getParameter(k));
            }
            return kv;
        }

        // forward转发的时候, 用得到
        if (parameterType == View.class) {
            return req.getAttribute("_view");
        }

        // 类上存在ReqAtta接口
        if (ReqAtta.class.isAssignableFrom(parameterType)) {
            WebApplicationContext webctx = (WebApplicationContext) req.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);
            try {
                ReqAtta reqAtta = (ReqAtta) webctx.getBean(parameterType);
                reqAtta.init(req);
                return reqAtta;
            } catch (Exception e) {
                log.error(e);
                return null;
            }
        }


        // @CookieValue
        if (parameter.hasMethodAnnotation(CookieValue.class)) {
            return ServletUtil.getCookie(req, parameterName);
        }

        String[] uri = ((String) req.getAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).split("/");

        // PathVariable
        String matchedURI = (String) req.getAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE);
        // 是否包含 { }
        if (matchedURI.contains("{") && matchedURI.contains("{")) {
            String[] splitMatchUri = matchedURI.split("/");
            for (int i = 0; i < splitMatchUri.length; i++) {
                String matchSubUri = splitMatchUri[i];
                if (matchSubUri.equals("")) continue;
                // 匹配到了
                if (ReUtil.isMatch(matchSubUri.replace("{", "\\{").replace("}", "\\}"), "{" + parameterName + "}")) {
                    value = Convert.convert(parameterType, uri[i]);
                    return value;
                }
            }
        }


        // 如果拿不到值
        String v1 = req.getParameter(parameterName);
        if (v1 == null) {

            String prefix = parameterName + ".";

            // 先判断是否有匹配的前缀, 如果没有直接return
            boolean flag = false;
            Enumeration<String> parameterNames = req.getParameterNames();
            while (parameterNames.hasMoreElements()) {
                String nextElement = parameterNames.nextElement();
                if (nextElement.startsWith(prefix)) {
                    flag = true;
                    break;
                }
            }
            if (!flag) return null;

            // 创建对象
            value = parameterType.newInstance();

            // 新建迭代器
            Enumeration<String> parameterNames2 = req.getParameterNames();
            while (parameterNames2.hasMoreElements()) {
                String nextElement = parameterNames2.nextElement();
                if (nextElement.startsWith(prefix)) {

                    String setterName = "set" + StrUtil.upperFirst(StrUtil.toCamelCase(nextElement.replaceFirst(prefix, "")));
                    // 获取 set 方法
                    Method setter = ReflectUtil.getMethodByName(parameterType,setterName);
                    // 如果setter方法为null, 跳过
                    if (setter == null) {
                        continue;
                    }

                    String[] parameterValues = req.getParameterValues(nextElement);
                    if (parameterValues != null) {
                        // setter的第一个参数类型
                        Class<?> parameterType1 = setter.getParameterTypes()[0];
                        // 如果是array类型
                        if (parameterType1.isArray()) {
                            Class<?> componentType = parameterType1.getComponentType();
                            Object[] arrayvalue = (Object[]) Array.newInstance(componentType, parameterValues.length);
                            for (int i = 0; i < parameterValues.length; i++) {
                                String parameterValue = parameterValues[i];
                                arrayvalue[i] = Convert.convert(componentType, parameterValue);
                            }
                            setter.invoke(value, new Object[]{arrayvalue});
                            //setter.invoke(value, arrayvalue);
                        } else {
                            Object convertvalue = Convert.convert(parameterType1, parameterValues[0]);
                            setter.invoke(value, convertvalue);
                        }

                    }
                }
            }

        } else {
            value = Convert.convert(parameterType, v1);
        }

        return value;
    }


}
